import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

public class NIOServer extends Server {

	public static final int BUF_SIZE = 20;
	public static final int MaxAccepts = 3;
	private static Selector selector;
	private static Map<SocketChannel, String> clientsSockets = new HashMap<SocketChannel, String>();

	// folosite pentru a trimite mesaje cu identificator; inca nu l-am pus si pe
	// asta
	public static final String startClient = "Client";
	public static final String stopClient = "ClientStop";

	private int portC;
	private int portP;

	private String received;

	public NIOServer(int portClient, int portPrinter) {
		this.portC = portClient;
		this.portP = portPrinter;
	}

	@Override
	public void run() {
		System.out.println("Simple Server started");
		ServerSocketChannel serverSocketChannel = null;

		selector = null;

		try {
			selector = Selector.open();

			serverSocketChannel = ServerSocketChannel.open();
			serverSocketChannel.configureBlocking(false);
			serverSocketChannel.socket()
					.bind(new InetSocketAddress(this.portC));
			serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

			while (true) {
				selector.select();

				for (Iterator<SelectionKey> it = selector.selectedKeys()
						.iterator(); it.hasNext();) {
					SelectionKey key = it.next();
					it.remove();

					if (key.isAcceptable()) {
						accept(key);
					} else if (key.isReadable()) {
						read(key);
					} else if (key.isWritable()) {
						write(key);
					}
				}
			}

		} catch (IOException e) {
			e.printStackTrace();

		} finally {
			if (selector != null) {
				try {
					selector.close();
				} catch (IOException e) {
				}
			}

			if (serverSocketChannel != null) {
				try {
					serverSocketChannel.close();
				} catch (IOException e) {
				}
			}
		}
	}

	public static void accept(SelectionKey key) throws IOException {

		System.out.print("ACCEPT: ");

		ServerSocketChannel serverSocketChannel = (ServerSocketChannel) key
				.channel();
		SocketChannel socketChannel = serverSocketChannel.accept();

		clientsSockets.put(socketChannel, "noname");

		socketChannel.configureBlocking(false);
		ByteBuffer buf = ByteBuffer.allocateDirect(BUF_SIZE);
		socketChannel.register(key.selector(), SelectionKey.OP_READ, buf);

		System.out.println("[NIOSimpleServer]Connection from: "
				+ socketChannel.socket().getRemoteSocketAddress());
	}

	public static void read(final SelectionKey key) throws IOException {
		System.out.println("READ: ");
		ByteBuffer buffer = ByteBuffer.allocate(20);
		int bytesRead = 0;
		SocketChannel socketChannel = (SocketChannel) key.channel();
		if ((bytesRead = socketChannel.read(buffer)) > 0) {
			buffer.flip();
			System.out.println("[NIOSimpleServer] Am citit: "
					+ Charset.defaultCharset().decode(buffer));
			buffer.clear();
		}
		if (bytesRead < 0) {
			// the key is automatically invalidated once the
			// channel is closed
			socketChannel.close();
		}
		key.interestOps(SelectionKey.OP_WRITE);
	}

	public static void write(SelectionKey key) throws IOException {
		System.out.println("WRITE: ");
		SocketChannel socketChannel = (SocketChannel) key.channel();
		CharBuffer buffer = CharBuffer.wrap("Hello from Simple Server!");
		while (buffer.hasRemaining()) {
			socketChannel.write(Charset.defaultCharset().encode(buffer));
		}
		key.interestOps(SelectionKey.OP_READ);
	}

	public static void main(String... args) {
		NIOServer server = new NIOServer(50003, 50002);
		server.run();
	}

}
